/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "imageknife_node_custom.h"
#include "imageknife_dispatcher.h"
#include "render/imageknife_render.h"
#include "imageknife_internal.h"
#include <arkui/drag_and_drop.h>

namespace ImageKnifePro {

ImageKnifeNodeCustom::ImageKnifeNodeCustom(std::shared_ptr<ImageKnifeOption> option)
    : ImageKnifeNodeImage(option, GetNodeAPI()->createNode(ARKUI_NODE_CUSTOM))
{
    IMAGE_KNIFE_LOG(LOG_DEBUG, "Create Custom Node");
    GetNodeAPI()->addNodeCustomEventReceiver(handle, OnCustomNodeEvent);
    GetNodeAPI()->registerNodeCustomEvent(handle, ARKUI_NODE_CUSTOM_EVENT_ON_DRAW, 0, this);
    GetNodeAPI()->registerNodeCustomEvent(handle, ARKUI_NODE_CUSTOM_EVENT_ON_MEASURE, 0, this);
    onImageComplete_ = option->onComplete;

    ArkUI_NumberValue renderFit = {.i32 = ARKUI_RENDER_FIT_RESIZE_FILL};
    ArkUI_AttributeItem item = {.value = &renderFit, .size = 1};
    GetNodeAPI()->setAttribute(handle, NODE_RENDER_FIT, &item);
}

ImageKnifeNodeCustom::~ImageKnifeNodeCustom()
{
    GetNodeAPI()->removeNodeCustomEventReceiver(handle, OnCustomNodeEvent);
    GetNodeAPI()->unregisterNodeCustomEvent(handle, ARKUI_NODE_CUSTOM_EVENT_ON_DRAW);
    GetNodeAPI()->unregisterNodeCustomEvent(handle, ARKUI_NODE_CUSTOM_EVENT_ON_MEASURE);
    if (registeredDragEvents_) {
        UnRegisterDragEvents();
    }
}

void ImageKnifeNodeCustom::Execute()
{
    auto request = GetImageKnifeRequest();
    IMAGE_KNIFE_CHECK(request == nullptr);
    auto option = request->GetImageKnifeOption();
    IMAGE_KNIFE_CHECK(option == nullptr);

    ImageKnifeInternal::GetInstance().Execute(request);
    if (!IsAdaptable() && option->downSampling != DownSamplingStrategy::DEFAULT &&
        option->downSampling != DownSamplingStrategy::NONE) {
        // 立即上树
        UploadNodeHandle();
    }
}

void ImageKnifeNodeCustom::OnCustomNodeEvent(ArkUI_NodeCustomEvent *event)
{
    auto customNode = reinterpret_cast<ImageKnifeNodeCustom *>(OH_ArkUI_NodeCustomEvent_GetUserData(event));
    auto type = OH_ArkUI_NodeCustomEvent_GetEventType(event);
    switch (type) {
        case ARKUI_NODE_CUSTOM_EVENT_ON_DRAW :
            customNode->OnDraw(event);
            break;
        case ARKUI_NODE_CUSTOM_EVENT_ON_MEASURE :
            customNode->OnMeasure(event);
            break;
        default :
            break;
    }
}

Image_Size ImageKnifeNodeCustom::GetComponentSize()
{
    return {.width = width_,
            .height = height_};
}

void ImageKnifeNodeCustom::OnMeasure(ArkUI_NodeCustomEvent *event)
{
    // 注意这里的单位是px 而不是vp
    auto layoutConstrain = OH_ArkUI_NodeCustomEvent_GetLayoutConstraintInMeasure(event);
    auto maxHeight = OH_ArkUI_LayoutConstraint_GetMaxHeight(layoutConstrain);
    auto maxWidth = OH_ArkUI_LayoutConstraint_GetMaxWidth(layoutConstrain);

    IMAGE_KNIFE_CHECK(maxHeight == 0 || maxWidth == 0);
    IMAGE_KNIFE_LOG(LOG_DEBUG, "On Layout MaxSize, width:%{public}d height:%{public}d", maxWidth, maxHeight);
    auto request = GetImageKnifeRequestInternal();
    IMAGE_KNIFE_CHECK(request == nullptr);
    if (IsAdaptable()) {
        // 组件宽高适应图片尺寸
        if (renderPixelmap_ == nullptr) {
            return;
        }

        Image_Size imageSize = PixelmapUtils::GetImageSize(renderPixelmap_);
        IMAGE_KNIFE_CHECK(imageSize.width == 0 || imageSize.height == 0);

        float imageRatio =  imageSize.width / static_cast<float>(imageSize.height);
        float layoutRatio = maxWidth / static_cast<float>(maxHeight);
        if (imageRatio > layoutRatio) {
            // 图片更宽，组件更窄，以宽度为基准
            width_ = maxWidth;
            height_ = imageSize.height / static_cast<float>(imageSize.width) * width_;
        } else {
            // 图片更窄或比例一致，组件更宽，以高度为基准
            height_ = maxHeight;
            width_ = imageRatio * height_;
        }
        GetNodeAPI()->setMeasuredSize(handle, width_, height_);
        request->SetComponentSize(GetComponentSize());
    } else {
        // 2. 如果是降采样请求，则测算完成后开始加载
        GetNodeAPI()->setMeasuredSize(handle, maxWidth, maxHeight);
        width_ = maxWidth;
        height_ = maxHeight;

        request->SetComponentSize(GetComponentSize());
        ImageKnifeDispatcher::GetInstance().EnqueueMainSrc(request);
    }
    OnMeasureComplete();
}

void ImageKnifeNodeCustom::OnDraw(ArkUI_NodeCustomEvent *event)
{
    if (needClear_) {
        needClear_ = false;
        IMAGE_KNIFE_LOG(LOG_DEBUG, "[ID:%{public}s] Custom Node Clear", GetComponentId().c_str());
        return;
    }

    // clear时资源被清空后不进行绘制
    if (renderPixelmap_ == nullptr) {
        return;
    }

    IMAGE_KNIFE_LOG(LOG_DEBUG, "[ID:%{public}s] Custom Node On Draw", GetComponentId().c_str());
    EventImage eventImage;
    ImageKnifeRender::Drawing(event, renderPixelmap_, {.imageFit = imageFit_,
        .colorFilter = colorFilter_ == nullptr ? nullptr : colorFilter_->GetColorFilter(),
        .border = GetComponentBorderOption(), .eventImage = &eventImage});
    if (onImageComplete_ != nullptr && triggerOnCompleteCallback_) {
        triggerOnCompleteCallback_ = false;
        ImageKnifeTrace trace("DrawingComplete", GetImageKnifeRequestInternal());
        trace.StartSyncTrace();
        onImageComplete_(eventImage);
        eventImage.loadingStatus = 1;
        // 维持原Image组件触发两次的逻辑
        onImageComplete_(eventImage);
    }
}

bool ImageKnifeNodeCustom::DrawingFrame(std::shared_ptr<ImageDataCacheInternal> dataCache, int frameCount)
{
    auto data = dataCache->GetImageData();
    IMAGE_KNIFE_CHECK_RETURN(data == nullptr, false);
    IMAGE_KNIFE_CHECK_RETURN(frameCount < 0 || frameCount > dataCache->GetFrameCount() - 1, false);
    IMAGE_KNIFE_CHECK_RETURN(data->GetPixelmapList() == nullptr, false);
    // 维持生命周期
    dataCache_ = dataCache;
    renderPixelmap_ = data->GetPixelmapList()[frameCount];
    if (UploadNodeHandle()) {
        // 节点已经上树
        if (IsAdaptable()) {
            GetNodeAPI()->markDirty(handle, NODE_NEED_MEASURE);
        }
        needClear_ = false;
        GetNodeAPI()->markDirty(handle, NODE_NEED_RENDER);
    }

    return true;
}

void ImageKnifeNodeCustom::Draggable(bool draggable)
{
    if (draggable && !registeredDragEvents_) {
        RegisterDragEvents();
        registeredDragEvents_ = true;
    } else if (!draggable && registeredDragEvents_) {
        UnRegisterDragEvents();
        registeredDragEvents_ = false;
    }
}

void ImageKnifeNodeCustom::FitImage(int fitEnum)
{
    imageFit_ = fitEnum;
}

void ImageKnifeNodeCustom::SetColorFilter(std::shared_ptr<ImageKnifeColorFilter> colorFilter)
{
    IMAGE_KNIFE_CHECK(colorFilter == nullptr, "OH_Drawing_ColorFilter Is NULL");
    colorFilter_ = colorFilter;
}

void ImageKnifeNodeCustom::RemoveColorFilter()
{
    colorFilter_ = nullptr;
}

// 覆盖父类ImageKnifeNodeImage虚函数
// 因为Image组件的bug，仅更新border需要清空重设组件图片资源，而custom node无需这么做
void ImageKnifeNodeCustom::RefreshImage()
{
}

void ImageKnifeNodeCustom::SetResizeableOption(std::shared_ptr<ResizableOption> resizeable)
{
}

void ImageKnifeNodeCustom::RemoveResizeableOption()
{
}

void ImageKnifeNodeCustom::RefreshDrawing()
{
    GetNodeAPI()->markDirty(handle, NODE_NEED_RENDER);
}

void ImageKnifeNodeCustom::RemoveDisplayedImage()
{
    needClear_ = true;
    renderPixelmap_ = nullptr;
    dataCache_ = nullptr;
    GetNodeAPI()->markDirty(handle, NODE_NEED_RENDER);
}

void ImageKnifeNodeCustom::RegisterDragEvents()
{
    GetNodeAPI()->registerNodeEvent(handle, NODE_ON_DRAG_START, 0, nullptr);
    GetNodeAPI()->registerNodeEvent(handle, NODE_ON_DRAG_END, 0, nullptr);
}

void ImageKnifeNodeCustom::UnRegisterDragEvents()
{
    GetNodeAPI()->unregisterNodeEvent(handle, NODE_ON_DRAG_START);
    GetNodeAPI()->unregisterNodeEvent(handle, NODE_ON_DRAG_END);
}

void ImageKnifeNodeCustom::OnDerivedNodeEvent(ArkUI_NodeEvent *event)
{
    auto eventType = OH_ArkUI_NodeEvent_GetEventType(event);
    switch (eventType) {
        case NODE_ON_DRAG_START:
            OnDragStart(event);
            break;
        case NODE_ON_DRAG_END:
            OnDragEnd(event);
            break;
        default: return;
    }
}

void ImageKnifeNodeCustom::OnDragStart(ArkUI_NodeEvent *event)
{
    IMAGE_KNIFE_LOG(LOG_DEBUG, "[ID:%{public}s]  On Drag Start", GetComponentId().c_str());
    IMAGE_KNIFE_CHECK(renderPixelmap_ == nullptr);

    dragData_.record = OH_UdmfRecord_Create();
    int returnValue;

    dragData_.udsPixelmap = OH_UdsPixelMap_Create();
    returnValue = OH_UdsPixelMap_SetPixelMap(dragData_.udsPixelmap, renderPixelmap_);
    IMAGE_KNIFE_CHECK(returnValue != 0, "Set UDS Pixelmap Failed");
    returnValue = OH_UdmfRecord_AddPixelMap(dragData_.record, dragData_.udsPixelmap);
    IMAGE_KNIFE_CHECK(returnValue != 0, "Add UDS Pixelmap To Record Failed");
    dragData_.data = OH_UdmfData_Create();

    returnValue = OH_UdmfData_AddRecord(dragData_.data, dragData_.record);
    IMAGE_KNIFE_CHECK(returnValue != 0, "UDMF Data Add Record Failed");
    auto dragEvent = OH_ArkUI_NodeEvent_GetDragEvent(event);
    returnValue = OH_ArkUI_DragEvent_SetData(dragEvent, dragData_.data);
}

void ImageKnifeNodeCustom::OnDragEnd(ArkUI_NodeEvent *event)
{
    IMAGE_KNIFE_LOG(LOG_DEBUG, "[ID:%{public}s]  On Drag End", GetComponentId().c_str());
    if (dragData_.data != nullptr) {
        OH_UdmfData_Destroy(dragData_.data);
        dragData_.data = nullptr;
    }

    if (dragData_.udsPixelmap != nullptr) {
        OH_UdsPixelMap_Destroy(dragData_.udsPixelmap);
        dragData_.udsPixelmap = nullptr;
    }

    if (dragData_.record != nullptr) {
        OH_UdmfRecord_Destroy(dragData_.record);
        dragData_.record = nullptr;
    }
}

void ImageKnifeNodeCustom::UpdateDerivedNodeEvent(std::shared_ptr<ImageKnifeOption> option)
{
    IMAGE_KNIFE_CHECK(option == nullptr, "Null ImageKnifeOption");
    onImageComplete_ = option->onComplete;
}
}

